import { unicodeRegExp } from "../utils/lang.js";

// Regular Expressions for parsing tags and attributes
const attribute =
  /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; //可以匹配的属性 a = "b" 或者 a = 'b' 或者 a=c
const dynamicArgAttribute =
  /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*`;
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
const startTagOpen = new RegExp(`^<${qnameCapture}`); //匹配的 <namespace:xxx 或者 <xxx
const startTagClose = /^\s*(\/?)>/; //匹配的 <xxx> 或者 <xxx/>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); //匹配的</xxx>
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; //匹配{{xxx}}

let currentParent;
let root;
let stack = [];
function makeAttrsMap(attrs) {
  const map = {};
  for (let i = 0, l = attrs.length; i < l; i++) {
    map[attrs[i].name.replace(/\s/g, "")] = attrs[i].value.replace(/\s/g, "");
  }
  return map;
}

function createASTElement(tag, attrs, parent) {
  return {
    type: 1,
    tag,
    attrsList: attrs,
    attrsMap: makeAttrsMap(attrs),
    // rawAttrsMap: {},
    parent,
    children: [],
  };
}

export function parseHTML(html) {
  let index = 0;
  function advance(n) {
    index += n;
    html = html.substring(n);
  }

  function start(tag, arrts) {
    const element = createASTElement(tag, arrts);
    if (!root) {
      root = element;
    }
    if (currentParent) {
      element.parent = currentParent;
      currentParent.children.push(element);
    }
    stack.push(element);
    currentParent = element;
  }
  function chars(text) {
    text = text.replace(/\s/g, "");
    if (text) {
      currentParent.children.push({
        type: 3,
        text,
      });
    }
  }
  function end(tag) {
    const element = stack.pop();
    currentParent = stack[stack.length - 1];
  }

  function parseStartTag() {
    const start = html.match(startTagOpen);
    if (start) {
      const match = {
        tagName: start[1],
        attrs: [],
        start: index,
      };
      advance(start[0].length);
      let end, attr;
      while (
        !(end = html.match(startTagClose)) &&
        (attr = html.match(dynamicArgAttribute) || html.match(attribute))
      ) {
        attr.start = index;
        advance(attr[0].length);
        attr.end = index;
        match.attrs.push(attr);
      }
      if (end) {
        match.unarySlash = end[1];
        advance(end[0].length);
        match.end = index;
        return match;
      }
    }
  }
  while (html) {
    const textEnd = html.indexOf("<");
    if (textEnd === 0) {
      const startTagMatch = parseStartTag(html);
      const endTagMatch = html.match(endTag);
      if (startTagMatch) {
        const l = startTagMatch.attrs.length;
        const attrs = new Array(l);
        for (let i = 0; i < l; i++) {
          const args = startTagMatch.attrs[i];
          const value = args[3] || args[4] || args[5] || "";
          attrs[i] = {
            name: args[1].replace(/\s/g, ""),
            value: value.replace(/\s/g, ""),
          };
        }
        start(startTagMatch.tagName, attrs);
        continue;
      }
      if (endTagMatch) {
        advance(endTagMatch[0].length);
        end(endTagMatch[1]);
        continue;
      }
    }

    if (textEnd > 0) {
      let text = html.substring(0, textEnd);
      if (text) {
        advance(text.length);
        chars(text);
      }
      continue;
    }
  }
  return root;
}
